import type { Mapless } from '..';
import type { IdHubType } from '../core/IdHub';
import IdHub from '../core/IdHub';
import type { Handler } from '../core/Listener';
import { EmitManager } from '../core/manager';
import { getOption } from '../core/utils';
import type { Node, NodeManagerEvents, NodeManagerOptions } from '../index.d';

type NodeIdHubType = IdHubType & {
  nodeLayerId: string;
  nodeLayerSelectId: string;
  nodeSourceId: string;
};

export default class NodeManager extends EmitManager<
  NodeManagerEvents,
  NodeIdHubType
> {
  private nodes: Node[] | undefined = undefined;

  private iconSize: number = 0.6;

  constructor(mapless: Mapless, options?: NodeManagerOptions) {
    super(mapless, options, 'node');
    this.onInitialized();
    // 设置是否显示文字，默认为false
    this.iconSize = getOption<number>('iconSize', options, 0.6);
    this.setupClickHandler();
    this.setupHoverHandler();
    this.nodes = [];
  }

  override createIdHub(_idHub?: IdHubType) {
    const newHub = new IdHub({
      layers: {
        nodeLayerId: 'dzmap-node-layer',
        nodeLayerSelectId: 'dzmap-node-select-layer',
      },
      sources: {
        nodeSourceId: 'dzmap-node-source',
      },
    }) as NodeIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    const sid = this.idHub?.nodeSourceId;
    this.mapless.addEmptySource(sid);
    this.mapless.addLayer({
      id: this.idHub?.nodeLayerSelectId,
      type: 'circle',
      source: sid,
      // filter: ['==', ['get', 'selected'], true],
      paint: {
        'circle-radius': 24, // 保持与普通图标相同大小
        'circle-color': [
          'case',
          ['==', ['get', 'selected'], true],
          'rgba(0, 120, 255, 0.3)',
          'rgba(0, 120, 255, 0.1)',
        ], // 半透明蓝色
        'circle-stroke-width': [
          'case',
          ['==', ['get', 'selected'], true],
          3,
          1,
        ], // 增加边框宽度
        'circle-stroke-color': '#1890ff', // 蓝色边框
      },
    });
    this.mapless.addLayer({
      id: this.idHub?.nodeLayerId,
      type: 'symbol',
      source: sid,
      layout: {
        'icon-image': ['case', ['has', 'icon'], ['get', 'icon'], 'deafult-6'],
        'icon-size': ['get', 'iconSize'],
        'text-field': ['get', 'name'],
        'text-font': ['Open Sans Regular', 'Arial Unicode MS Regular'],
        'text-offset': [0, 2.0],
        'text-anchor': 'top',
        'icon-allow-overlap': true, // 允许图标重叠
        'text-allow-overlap': true, // 允许文字重叠
        'icon-ignore-placement': true, // 忽略图标位置冲突
        'text-ignore-placement': true, // 忽略文字位置冲突
      },
      paint: {
        'text-color': 'rgba(151, 151, 151, 0.8)',
        'text-halo-color': 'rgba(255, 255, 255, 0.3)',
        'text-halo-width': 1, // 阴影宽度
        'text-halo-blur': 0, // 阴影模糊度
        // TODO experimental
        // "symbol-z-offset": ["get", "altitude"]
      },
    });
  }

  drawNodes(nodes: Node[]) {
    // 缓存当前nodes
    this.nodes = nodes;
    // 创建GeoJSON数据
    const features = nodes.map((node) => ({
      type: 'Feature',
      properties: {
        id: node.id,
        icon: node.icon,
        name: node.name,
        altitude: node.altitude,
        selected: node.selected,
        iconSize: this.iconSize,
      },
      geometry: {
        type: 'Point',
        coordinates: [node.longitude, node.latitude],
      },
    }));
    const data = { type: 'FeatureCollection', features } as GeoJSON.GeoJSON;
    this.mapless?.setData(this.idHub?.nodeSourceId, data);
  }

  selectNodes(ids: string[]) {
    if (!this.nodes) return;
    this.nodes.forEach((node) => {
      node.selected = ids.includes(node.id);
    });
    this.drawNodes(this.nodes);
  }

  override destroy = () => {
    super.destroy();
    this.nodes = undefined;
  };

  setupClickHandler() {
    if (!this.map) return;
    const clickHandler = (e: mapboxgl.MapMouseEvent) => {
      if (!this.map) return;
      const feature = e.features?.[0] as GeoJSON.Feature<GeoJSON.Point>;
      if (feature) {
        const curr = this.nodes?.find(
          (node) => node.id === feature.properties?.id,
        );
        curr && this.emit('nodeClick', curr);
        curr && this.selectNodes([curr.id]);
      }
    };
    this._listener?.add(
      'click',
      this.idHub?.nodeLayerId,
      clickHandler as Handler,
    );
  }

  setupHoverHandler() {
    if (!this.map) return;
    const enterHandler = (e: mapboxgl.MapMouseEvent) => {
      const feature = e.features?.[0];
      if (feature) {
        this.mapless?.setCursor('pointer');
      }
    };
    this._listener?.add(
      'mouseenter',
      this.idHub?.clusterLayerSelectId,
      enterHandler as Handler,
    );
    const leaveHandler = () => {
      this.mapless?.setCursor('');
    };
    this._listener?.add(
      'mouseleave',
      this.idHub?.clusterLayerSelectId,
      leaveHandler as Handler,
    );
  }
}

export const attachNode = (
  mapless: Mapless | undefined,
  name?: string,
  options?: NodeManagerOptions,
) => mapless?.use(NodeManager, name, options);
export const getNode = (mapless: Mapless | undefined, name?: string) =>
  mapless?.get(NodeManager, name);
export const detachNode = (mapless: Mapless | undefined, name?: string) =>
  mapless?.unuse(NodeManager, name);
